﻿/* --------------------------------------------------------------
 * exMath (C) Laurens Ruijtenberg
 * 
 * This code is being distributed under the MS-PL license. Feel
 * free to contact me if you like this code.
 * -------------------------------------------------------------- */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using exMath.Common;

namespace exMath.Calculus
{
    /// <summary>
    /// Represents x mathematical limit. The limit of f(x), as x approaches a, equals L.
    /// </summary>
    public class Limit
    {
        #region Properties
        
        public double a { get; private set; }
        public double x { get; private set; }
        public double L { get; private set; }

        public MathFunction Function { get; private set; }

        #endregion

        #region operators

        public static bool operator ==(Limit l1, Limit l2)
        {
            if (l1.a == l2.a && l1.x == l2.x && l1.L == l2.L)
                return true;
            else return false;
        }

        public static bool operator ==(Limit l1, double l2)
        {
            return l1.L == l2;
        }

        public static bool operator !=(Limit l1, Limit l2)
        {
            return !(l1 == l2);
        }

        public static bool operator !=(Limit l1, double l2)
        {
            return !(l1 == l2);
        }

        #endregion

        #region Private methods


        #endregion

        #region Public methods

        /// <summary>
        /// Executes the corresponding Function of this limit while enforcing it's boundaries.
        /// </summary>
        /// <param name="Interval"></param>
        /// <returns></returns>
        public double[] EnforcedExecute(double Interval)
        {
            if (Function != null)
            {
                if (x == double.PositiveInfinity)
                    return Functions.ExecuteFunction(a, double.MaxValue, Interval, Function);
                else
                    return Functions.ExecuteFunction(a, x, Interval, this.Function);
            }
            else
            {
                throw new Exception("No Function defined for limit.");
            }
        }

        #endregion

        #region Static methods
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Function"></param>
        /// <param name="x"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static Limit CreateLimit(MathFunction Function, double x, double a)
        {
            Limit FunctionLimit = new Limit();

            FunctionLimit.L = Function(a);
            FunctionLimit.a = a;
            FunctionLimit.x = x;
            FunctionLimit.Function = Function;

            return FunctionLimit;

        }

        /// <summary>
        /// [depricated]
        /// </summary>
        /// <param name="DomainStart"></param>
        /// <param name="DomainEnd"></param>
        /// <param name="DomainInterval"></param>
        /// <param name="Function"></param>
        /// <returns></returns>
        /*public static Limit[] GetLimits(double DomainStart, double DomainEnd, double DomainInterval, MathFunction Function)
        {
            List<Limit> Limits = new List<Limit>();

            double[] DomainValues = Functions.ToDomainIntervalArray(DomainStart, DomainEnd, DomainInterval);
            double[] Results = Functions.ExecuteFunction(DomainValues, Function);

            double PreviousStep = 0.0;
            double h = 0.0;
            
            int CurrentLimitIndex = 0;

            Limit FirstLimit = new Limit();
            FirstLimit.x = DomainValues[0];
            FirstLimit.Function = Function;

            //Walk through each result value of the function and detect if the result direction changes.
            //If so, x new limit is created and the current one is ended.
            for (int i = 0; i < DomainValues.Length; i++)
            {
                if (i > 0)
                    h = Results[i] - Results[i - 1];
                else
                    h = Results[i];

                //if the direction of the function results change, start x new limit
                if ((h < 0.0 && PreviousStep > 0.0) || (h > 0.0 && PreviousStep < 0.0))
                {
                    //first finish previous Limit
                    Limits[CurrentLimitIndex].L = Results[i - 1];
                    Limits[CurrentLimitIndex].x = DomainValues[i - 1];

                    //create new limit
                    Limit NewLimit = new Limit();
                    NewLimit.x = DomainValues[i];
                    NewLimit.Function = Function;
                    Limits.Add(NewLimit);

                    CurrentLimitIndex++;
                }

                PreviousStep = h;
            }

            //Finish the final limit
            Limits[CurrentLimitIndex].L = Results[Results.Length - 1];
            Limits[CurrentLimitIndex].x = DomainValues[DomainValues.Length - 1];

            return Limits.ToArray();
        }*/

        public static bool CompliesToLimit(MathFunction Function, Limit FunctionLimit)
        {
            if (Function(FunctionLimit.a) == FunctionLimit.L)
                return true;
            else
                return false;
        }

        #endregion

        #region Overrided methods
        public override bool Equals(object obj)
        {
            if (obj is Limit)
            {
                return this == (Limit)obj;
            }
            else
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion
    }
}
